﻿package kit4j.ui.cs.swing.component;

import java.util.HashMap;
import java.util.Map;

import javax.swing.JList;

import kit4j.ui.cs.FontSize;
import kit4j.ui.cs.exception.ListItemNotFoundException;
import kit4j.ui.cs.exception.ListNoSelectionException;

/**
 * 显示字体尺寸列表并且允许用户选择一个或多个项的组件。使用方法类似JList。 <br />
 * 以下代码创建一个显示默认字体尺寸的列表并选择尺寸值为12的项，然后打印列表当前选择的字体尺寸信息：
 * 
 * <pre>
 * <code>
 * 	JFontSizeList list = new JFontSizeList();
 * 	list.setSelectedFontSize(new FontSize("12", 12));
 * 	System.out.println(fontList.getSelectedFontSize());
 * </code>
 * </pre>
 * 
 * @author MiniKnife
 */
public class JFontSizeList extends JList {

	private static final long serialVersionUID = 1L;

	/**
	 * 默认字体尺寸。
	 */
	public static final FontSize[] DEFAULT_FONT_SIZES = { new FontSize("8", 8), new FontSize("9", 9),
			new FontSize("10", 10), new FontSize("11", 11), new FontSize("12", 12), new FontSize("14", 14),
			new FontSize("16", 16), new FontSize("18", 18), new FontSize("20", 20), new FontSize("22", 22),
			new FontSize("24", 24), new FontSize("26", 26), new FontSize("28", 28), new FontSize("36", 36),
			new FontSize("48", 48), new FontSize("72", 72) };

	/**
	 * 字体尺寸名称-字体尺寸对应集。字体尺寸列表通过它使用字体尺寸名称定位到列表中的某个字体尺寸项。
	 */
	private Map<Integer, FontSize> fontSizeValueMap;

	public JFontSizeList() {
		this(null);
	}

	public JFontSizeList(FontSize[] fontSizes) {
		super();

		setCellRenderer(new JFontSizeListCellRenderer());

		fontSizeValueMap = new HashMap<Integer, FontSize>();

		if (fontSizes != null) {
			setListData(fontSizes);
		} else {
			setListData(DEFAULT_FONT_SIZES);
		}
	}

	/**
	 * 返回最小的选择单元索引的字体尺寸；只选择了列表中单个项时，返回所选值。选择了多项时，返回最小的选择索引的值。
	 * 
	 * @return 当所选的第一个字体尺寸
	 * @throws ListNoSelectionException
	 *             如果列表什么也没有选择，抛出此异常。
	 */
	public FontSize getSelectedFontSize() throws ListNoSelectionException {
		FontSize font = (FontSize) super.getSelectedValue();

		if (font == null) {
			throw new ListNoSelectionException();
		}

		return font;
	}

	/**
	 * 从列表中选择指定的字体尺寸。
	 * 
	 * @param fontSize
	 *            要选择的字体尺寸
	 * @param shouldScroll
	 *            如果所选字体尺寸存在，但列表需要滚动才能显示，则为 true；否则为 false
	 * @throws ListItemNotFoundException
	 *             如果列表中不存在此字体尺寸，则抛出此异常。
	 */
	public void setSelectedFontSize(FontSize fontSize, boolean shouldScroll) throws ListItemNotFoundException {
		setSelectedFontSizeByValue(fontSize.getValue(), shouldScroll);
	}

	/**
	 * 从列表中选择具有指定字体尺寸值的字体尺寸。
	 * 
	 * @param value
	 *            要选择的字体尺寸的值
	 * @param shouldScroll
	 *            如果所选字体尺寸存在，但列表需要滚动才能显示，则为 true；否则为 false
	 * @throws ListItemNotFoundException
	 *             如果列表中不存在此字体尺寸，则抛出此异常。
	 */
	public void setSelectedFontSizeByValue(int value, boolean shouldScroll) throws ListItemNotFoundException {
		FontSize f = fontSizeValueMap.get(value);

		if (f == null) {
			throw new ListItemNotFoundException();
		}

		super.setSelectedValue(f, shouldScroll);
	}

	/**
	 * 返回所有选择字体尺寸的数组，根据其列表中的索引顺序按升序排序。
	 * 
	 * @return 所选字体尺寸
	 * @throws ListNoSelectionException
	 *             如果列表什么也没有选择，抛出此异常。
	 */
	public FontSize[] getSelectedFontSizes() throws ListNoSelectionException {
		FontSize[] fonts = (FontSize[]) super.getSelectedValues();

		if (fonts.length == 0) {
			throw new ListNoSelectionException();
		}

		return fonts;
	}

	@Override
	/**
	 * 从列表中选择指定的字体尺寸。如果列表中不存在此字体尺寸，则什么也不做，建议使用替代方法
	 * JFontSizeList#setSelectedFontSize(FontSize, boolean)。
	 * @param font 要选择的字体尺寸
	 * @param shouldScroll 如果所选字体尺寸存在，但列表需要滚动才能显示，则为 true；否则为 false
	 * @throws IllegalArgumentException 如果参数对象为null或参数不是FontSize类型，抛出此异常。
	 */
	public void setSelectedValue(Object font, boolean shouldScroll) throws IllegalArgumentException {
		if (font == null) {
			throw new IllegalArgumentException("Argument should't be null!");
		}

		if (!(font instanceof FontSize)) {
			throw new IllegalArgumentException("Argument must be a FontSize");
		}

		try {
			setSelectedFontSize((FontSize) font, shouldScroll);
		} catch (ListItemNotFoundException e) {
			return;
		}
	}

	@Override
	/**
	 * 根据一个字体尺寸对象数组构造只读 ListModel，然后对此模型调用 setModel。 
	 * @throws IllegalArgumentException 如果参数对象为null或参数不是FontSize类型数组，抛出此异常。
	 */
	public void setListData(Object[] data) throws IllegalArgumentException {
		if (data == null) {
			throw new IllegalArgumentException("Argument should't be null!");
		}

		if (!(data instanceof FontSize[])) {
			throw new IllegalArgumentException("Argument must be a FontSize list");
		}

		super.setListData(data);

		// 初始化名称-字体尺寸对应集
		fontSizeValueMap.clear();
		for (FontSize fs : (FontSize[]) data) {
			fontSizeValueMap.put(fs.getValue(), fs);
		}
	}

	/**
	 * 返回字体尺寸列表中的字体尺寸对象数组。
	 * 
	 * @return 字体尺寸对象数组
	 */
	public FontSize[] getListFontSizes() {
		return fontSizeValueMap.values().toArray(new FontSize[0]);
	}

	/**
	 * 根据一个字体尺寸对象数组构造只读 ListModel，然后对此模型调用 setModel。
	 * 
	 * @throws IllegalArgumentException
	 *             如果参数对象为null或参数不是FontSize类型数组，抛出此异常。
	 */
	public void setListFontSizes(FontSize[] data) throws IllegalArgumentException {
		this.setListData(data);
	}
}
